import logging
import re
import sinodev
import sinodev.tools.list_ports
from logging.handlers import RotatingFileHandler
from PySide6.QtCore import Slot, Qt
from PySide6.QtGui import QCloseEvent, QBrush
from PySide6.QtSql import QSqlDatabase, QSqlQuery
from PySide6.QtWidgets import QWidget, QMessageBox, QFileDialog
from ui_mainform import Ui_FormART


ART_CREATE_TABLE = """CREATE TABLE IF NOT EXISTS art
(id CHAR(44) PRIMARY KEY,
 fr TEXT     NOT NULL);"""
ART_INSERT_TABLE = "INSERT OR REPLACE INTO art (id, fr) VALUES (?, ?);"
ART_SELECT_TABLE = "SELECT fr FROM art WHERE id = ?;"
ART_DB_PATH = "art.db"
ART_LOG_PATH = "art.log"
ART_LOG_FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s %(message)s"
ART_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
ART_LOG_MAX_SIZE = 1024 * 1024 * 10  # 10MB
ART_LOG_MAX_COUNT = 5
ART_DEVICE_KEYWORDS = ["USB", "Virtual Com Port"]


class MainForm(QWidget, Ui_FormART):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName(ART_DB_PATH)
        if not self.db.open():
            QMessageBox.critical(self, "Error", "Can't open database!")
            return

        self.query = QSqlQuery()
        if not self.query.exec(ART_CREATE_TABLE):
            self.db.close()
            QMessageBox.critical(self, "Error", "Can't create table!")
            return

        self.pushButtonUpdate.setEnabled(True)
        self.pushButtonRefresh.setEnabled(True)
        self.pushButtonRegister.setEnabled(True)

        self.logger = logging.getLogger("art")
        handler = RotatingFileHandler(
            filename=ART_LOG_PATH,
            maxBytes=ART_LOG_MAX_SIZE,
            backupCount=ART_LOG_MAX_COUNT,
            encoding="utf-8",
        )
        formatter = logging.Formatter(fmt=ART_LOG_FORMAT, datefmt=ART_LOG_DATE_FORMAT)
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)

        self.rtks = []

    @Slot()
    def on_pushButtonUpdate_clicked(self):
        fp = QFileDialog.getOpenFileName(
            self, "Update Database", "", "Text Files (*.txt);;All Files (*)"
        )[0]
        if not fp:
            return

        with open(fp, "r", encoding="utf-8") as f:
            for line in f:
                if id := re.findall(sinodev.SinoCommand.P_I, line):
                    id = id[0]
                else:
                    continue
                if fr := re.findall(sinodev.SinoCommand.P_R, line):
                    fr = fr[0]
                else:
                    continue
                self.query.prepare(ART_INSERT_TABLE)
                self.query.addBindValue(id)
                self.query.addBindValue(fr)
                if not self.query.exec():
                    QMessageBox.warning(self, "Warning", f"Failed to insert data!")
                    return

        QMessageBox.information(self, "Information", "Done. Database updated.")

    @Slot()
    def on_pushButtonRefresh_clicked(self):
        self.rtks.clear()
        self.listWidgetComports.clear()

        try:
            for com in sinodev.tools.list_ports.comports(
                device_keywords=ART_DEVICE_KEYWORDS
            ):
                with sinodev.SinoRTK(com) as rtk:
                    rtk.sn_length = int(self.comboBoxSNLength.currentText())
                    rtk.hwv_length = int(self.comboBoxHWVLength.currentText())
                    if rtk.fill():
                        self.rtks.append(rtk)
                        self.listWidgetComports.addItem(f"{rtk.sn}@{com}")
        except Exception as e:
            QMessageBox.critical(self, "Error", str(e))
        else:
            QMessageBox.information(self, "Information", "Done. List refreshed.")

    @Slot()
    def on_pushButtonRegister_clicked(self):
        for i, rtk in enumerate(self.rtks):
            rtk: sinodev.SinoRTK
            self.logger.info(f"Registering {rtk.sn}@{rtk.port}...")
            self.logger.info(rtk.id)
            self.query.prepare(ART_SELECT_TABLE)
            self.query.addBindValue(rtk.id)
            self.query.exec()
            while self.query.next():
                if rtk.regist(self.query.value("fr")):
                    self.listWidgetComports.item(i).setBackground(
                        QBrush(Qt.GlobalColor.green, Qt.BrushStyle.SolidPattern)
                    )
                    self.logger.info(f"Succeed.")
                else:
                    self.listWidgetComports.item(i).setBackground(
                        QBrush(Qt.GlobalColor.red, Qt.BrushStyle.SolidPattern)
                    )
                    self.logger.info(f"Failed.")
                break
            else:
                self.logger.info("ID not found.")

            self.logger.info("=" * 10 + " Device Function Registry " + "=" * 10)
            for fr in rtk.fr:
                self.logger.info(f"{fr}")
            self.logger.info("=" * 46)
            self.logger.info("Done.")

        QMessageBox.information(self, "Information", "Done. Result saved.")

    def closeEvent(self, event: QCloseEvent):
        if self.db.isOpen():
            self.db.close()
        event.accept()
